package com.psedu.exam.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.psedu.base.api.RemoteStudentService;
import com.psedu.base.api.domain.dto.StudentDTO;
import com.psedu.exam.api.domain.vo.ExamBo;
import com.psedu.exam.api.domain.vo.SimpleExamVo;
import com.psedu.exam.constant.Constants;
import com.psedu.exam.domain.*;
import com.psedu.exam.domain.vo.*;
import com.psedu.exam.mapper.*;
import com.psedu.exam.service.IPaperService;
import com.psedu.exam.service.IQuestionService;
import com.psedu.common.core.constant.HttpStatus;
import com.psedu.common.core.constant.SecurityConstants;
import com.psedu.common.core.domain.R;
import com.psedu.common.core.exception.ServiceException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.psedu.exam.service.IExamService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 考试发起Service业务层处理
 * 
 * @author mingyue
 * @date 2022-03-30
 */
@Service
public class ExamServiceImpl implements IExamService 
{
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private ExamDeptStatusMapper examDeptStatusMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private IQuestionService questionService;

    @Autowired
    private IPaperService paperService;

    @Autowired
    private RemoteStudentService remoteStudentService;

    @Autowired
    private AnswerSheetMapper answerSheetMapper;

    @Autowired
    private PaperMapper paperMapper;

    /**
     * 查询考试发起
     * 
     * @param examId 考试发起主键
     * @return 考试发起
     */
    @Override
    public Exam selectExamByExamId(Long examId)
    {
        return examMapper.selectExamByExamId(examId);
    }

    /**
     * 查询考试发起列表
     * 
     * @param exam 考试发起
     * @return 考试发起
     */
    @Override
    public List<Exam> selectExamList(Exam exam)
    {
        return examMapper.selectExamList(exam);
    }

    /**
     * 新增考试发起，新加分党校状态
     * 
     * @param exam 考试发起
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertExamAndDeptStatus(Exam exam)
    {
        Long semeTypeExamCount = getSemeTypeExamCount(exam);
        if(0L < semeTypeExamCount) {
            throw new ServiceException("本学期已存在该类型考试");
        }
        int insertExamRes = examMapper.insert(exam);
        Long examId = exam.getExamId();
        // 分党校 TODO 远程调用
        List<Long> deptList = new ArrayList<>();
        for (long deptId = 200; deptId <= 228; deptId++) {
            deptList.add(deptId);
        }
        // 添加分党校考试状态
        List<ExamDeptStatus> examDeptStatusList = new ArrayList<>();
        for (Long deptId : deptList) {
            // 多轮考试
            for(int repeatCount = 1; repeatCount <= exam.getMaxRepeatCount(); repeatCount++) {
                ExamDeptStatus examDeptStatus = new ExamDeptStatus();
                examDeptStatus.setExamId(examId);
                examDeptStatus.setRepeatCount(repeatCount);
                examDeptStatus.setDeptId(deptId);
                examDeptStatus.setStatus(0);
                examDeptStatusList.add(examDeptStatus);
            }
        }
        boolean insertStatusRes = examDeptStatusMapper.insertBatch(examDeptStatusList);
        return true;
    }

    /**
     * 获取考试是否重复
     * @param exam 考试实体
     * @return 数量
     */
    private Long getSemeTypeExamCount(Exam exam) {
        return examMapper.selectCount(
                    new LambdaQueryWrapper<Exam>()
                            .eq(Exam::getSemeId, exam.getSemeId())
                            .eq(Exam::getOfficialExam, exam.getOfficialExam())
            );
    }

    /**
     * 修改考试发起
     * 
     * @param exam 考试发起
     * @return 结果
     */
    @Override
    public int updateExam(Exam exam)
    {
        return examMapper.updateById(exam);
    }


    /**
     * 批量删除考试发起
     * 
     * @param examIds 需要删除的考试发起主键
     * @return 结果
     */
    @Override
    public int deleteExamByExamIds(Long[] examIds)
    {
        return examMapper.deleteExamByExamIds(examIds);
    }

    /**
     * 删除考试发起信息
     * 
     * @param examId 考试发起主键
     * @return 结果
     */
    @Override
    public int deleteExamByExamId(Long examId)
    {
        return examMapper.deleteById(examId);
    }

    /**
     * 更改考试状态
     * @param examId 考试ID
     * @param status 新考试状态
     * @return 更改数量
     */
    @Override
    public int updateExamStatus(Long examId, Integer status) {
        Exam exam = new Exam();
        exam.setExamId(examId);
        exam.setAllowStart(status);
        return examMapper.updateById(exam);
    }

    /**
     * 通过学期Ids获取考试列表
     * @param semeIds 学期Ids
     * @return 考试列表
     */
    @Override
    public List<ExamBo> getExamsBySemeIds(Long deptId, List<Long> semeIds) {
        List<Exam> exams = examMapper.selectList(
                new LambdaQueryWrapper<Exam>()
                        .in(Exam::getSemeId, semeIds)
        );
        // 聚合党校考试信息
        List<ExamBo> examBos = exams.stream().map(exam -> {
            ExamBo examBo = new ExamBo();
            BeanUtils.copyProperties(exam, examBo);
            ExamDeptStatus examDeptStatus = examDeptStatusMapper.selectOne(
                    new LambdaQueryWrapper<ExamDeptStatus>()
                            .eq(ExamDeptStatus::getDeptId, deptId)
                            .eq(ExamDeptStatus::getExamId, exam.getExamId())
            );
            Integer status = examDeptStatus.getStatus();
            examBo.setStatus(status);
            examBo.setStartTime(examDeptStatus.getStartTime());
            examBo.setEndTime(examDeptStatus.getEndTime());
            return examBo;
        }).collect(Collectors.toList());
        return CollectionUtil.emptyIfNull(examBos);
    }

    @Override
    public List<SimpleExamVo> getExamSelectOptions() {
        return examMapper.selectVoList(null, SimpleExamVo.class);
    }

    @Override
    public List<ExamDetailVo> getExamDetailList(Long deptId) {
        List<ExamDetailVo> examDetailVos = examMapper.selectExamDetailList(deptId);
        return examDetailVos;
    }

    @Override
    public List<ExamDetailVo> getUserExamDetailList(Long deptId, Long userId) {
        R<List<Long>> passSemesterIdsR = remoteStudentService.getAllPassSemester(userId, SecurityConstants.INNER);
        List<Long> passSemesterIds = passSemesterIdsR.getData();
        if(CollectionUtil.isEmpty(passSemesterIds)) {
            return new ArrayList<>();
        }
        List<ExamDetailVo> examDetailVos = examMapper.selectStudentExamDetailList(deptId, passSemesterIds);
        return examDetailVos;
    }

    @Override
    public PaperVo getExamTestPaper(Integer trainObject) {
        PaperVo paperVo = new PaperVo();
        paperVo.setExamName("自我测试");
        paperVo.setStartTime(new Date());
        paperVo.setEndTime(DateUtil.offsetMinute(new Date(), 60));
        paperService.createPaperProblem(paperVo, trainObject);
        return paperVo;
    }

    @Override
    public MarkTestPaperVo submitTestPaper(ExamTestPaperAnswerVo examTestPaperAnswerVo) {
        List<AnswerQuestion> singleList = examTestPaperAnswerVo.getSingle();
        List<AnswerQuestion> judgeList = examTestPaperAnswerVo.getJudge();
        List<AnswerQuestion> multiList = examTestPaperAnswerVo.getMulti();
        MarkTestPaperVo markPaperVo = new MarkTestPaperVo();
        List<AnswerStatus> singleStatusList = markAnswer(singleList);
        markPaperVo.setSingleStatusList(singleStatusList);
        List<AnswerStatus> judgeStatusList = markAnswer(judgeList);
        markPaperVo.setJudgeStatusList(judgeStatusList);
        List<AnswerStatus> multiStatusList = markAnswer(multiList);
        markPaperVo.setMultiStatusList(multiStatusList);
        // 计分
        int score = countAnswerScore(singleStatusList, judgeStatusList, multiStatusList);
        markPaperVo.setScore(score);
        return markPaperVo;
    }

    private int countAnswerScore(List<AnswerStatus> singleStatusList, List<AnswerStatus> judgeStatusList, List<AnswerStatus> multiStatusList) {
        int score = 0;
        for (AnswerStatus answerStatus : singleStatusList) {
            score += answerStatus.getAnswerCorrect() ? Constants.SINGLE_PER_SCORE : 0;
        }
        for (AnswerStatus answerStatus : judgeStatusList) {
            score += answerStatus.getAnswerCorrect() ? Constants.JUDGE_PER_SCORE : 0;
        }
        for (AnswerStatus answerStatus : multiStatusList) {
            score += answerStatus.getAnswerCorrect() ? Constants.MULTI_PER_SCORE : 0;
        }
        return score;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public MarkPaperVo submitExamPaper(ExamPaperAnswerVo examPaperAnswerVo, Long userId) {
        // 检查是否在考试时间
        ExamDeptStatus examDeptStatus = examPreInspect(examPaperAnswerVo.getExamDeptStatusId());
        // 是否已经完成作答
        Long examId = examDeptStatus.getExamId();
        Exam exam = examMapper.selectById(examId);
        Long semeId = exam.getSemeId();
        R<StudentDTO> studentInfoR = remoteStudentService.getStudentInfo(semeId, userId, SecurityConstants.INNER);
        StudentDTO studentDto = studentInfoR.getData();
        Long applyId = studentDto.getApplyId();
        AnswerSheet answerSheet = answerSheetMapper.selectOne(
                new LambdaQueryWrapper<AnswerSheet>()
                        .eq(AnswerSheet::getApplyId, applyId)
                        .eq(AnswerSheet::getOfficialExam, exam.getOfficialExam())
                        .eq(AnswerSheet::getRepeatCount, examDeptStatus.getRepeatCount())
        );
        if( answerSheet == null ) {
            throw new ServiceException("您未开始考试", HttpStatus.FORBIDDEN);
        }
        // 已经作答
        if (answerSheet.getStatus() == Constants.ANSWER_SHEET_SUBMITTED) {
            throw new ServiceException("您已作答，不可重复作答", HttpStatus.FORBIDDEN);
        }
        // 评分
        MarkPaperVo markPaperVo = new MarkPaperVo();
        int score = countExamPaperScore(examPaperAnswerVo);
        markPaperVo.setScore(score);
        markPaperVo.setExamPassed(score >= Constants.PASS_SCORE);
        // 保存成绩，修改作答状态
        answerSheet.setScore( new BigDecimal(score) );
        answerSheet.setStatus(Constants.ANSWER_SHEET_SUBMITTED);
        answerSheet.setAnswerSheetJson(JSONObject.toJSONString(examPaperAnswerVo));
        answerSheetMapper.updateById(answerSheet);
        if(exam.getOfficialExam() == 1) {
            updateTrainExamScore(applyId, score);
        }
        return markPaperVo;
    }

    private void updateTrainExamScore(Long applyId, int score) {
        R r = remoteStudentService.updateExamStudentScore(
                applyId,
                new BigDecimal(score),
                SecurityConstants.INNER
        );
        if(HttpStatus.SUCCESS != r.getCode()) {
            throw new ServiceException("提交失败，请重试", HttpStatus.ERROR);
        }
    }

    private int countExamPaperScore(ExamPaperAnswerVo examPaperAnswerVo) {
        List<AnswerQuestion> singleList = examPaperAnswerVo.getSingle();
        List<AnswerQuestion> judgeList = examPaperAnswerVo.getJudge();
        List<AnswerQuestion> multiList = examPaperAnswerVo.getMulti();
        List<AnswerStatus> singleStatusList = markAnswer(singleList);
        List<AnswerStatus> judgeStatusList = markAnswer(judgeList);
        List<AnswerStatus> multiStatusList = markAnswer(multiList);
        // 计分
        return countAnswerScore(singleStatusList, judgeStatusList, multiStatusList);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public PaperVo enterExamAndGetPaper(Long examDeptStatusId, Long userId) {
        ExamDeptStatus examDeptStatus = examPreInspect(examDeptStatusId);
        // 检查考试是否已经作答
        Long examId = examDeptStatus.getExamId();
        Exam exam = examMapper.selectById(examId);
        Long semeId = exam.getSemeId();
        R<StudentDTO> studentInfoR = remoteStudentService.getStudentInfo(semeId, userId, SecurityConstants.INNER);
        StudentDTO studentDto = studentInfoR.getData();
        AnswerSheet answerSheet = answerSheetMapper.selectOne(
                new LambdaQueryWrapper<AnswerSheet>()
                        .eq(AnswerSheet::getApplyId, studentDto.getApplyId())
                        .eq(AnswerSheet::getOfficialExam, exam.getOfficialExam())
                        .eq(AnswerSheet::getRepeatCount, examDeptStatus.getRepeatCount())
        );
        // 已经生成试卷
        if(answerSheet != null) {
            if(answerSheet.getStatus() == Constants.ANSWER_SHEET_SUBMITTED) {
                throw new ServiceException("你已作答，不可重复作答，分数为" +answerSheet.getScore(), HttpStatus.FORBIDDEN);
            }
            Paper paper = paperMapper.selectById(answerSheet.getPaperId());
            PaperVo paperVo = paperService.getPaperVoByPaperId(paper);
            paperVo.setExamName(exam.getExamName());
            paperVo.setStartTime(examDeptStatus.getStartTime());
            paperVo.setEndTime(examDeptStatus.getStartTime());
            return paperVo;
        }
        // 考试的培训对象 TODO 建议优化
        return paperService.createExamPaper(examDeptStatus, examId, exam, semeId, studentDto);
    }

    private ExamDeptStatus examPreInspect(Long examDeptStatusId) {
        ExamDeptStatus examDeptStatus = examDeptStatusMapper.selectById(examDeptStatusId);
        if(examDeptStatus == null) {
            throw new ServiceException("考试不存在", HttpStatus.FORBIDDEN);
        }
        // 检查考试是否开始
        Integer examStatus = examDeptStatus.getStatus();
        this.assertDeptExamRunning(examStatus);
        return examDeptStatus;
    }

    private void assertDeptExamRunning(Integer examStatus) {
        if(Constants.DEPT_EXAM_END == examStatus) {
            throw new ServiceException("考试已结束", HttpStatus.FORBIDDEN);
        }
        if(Constants.DEPT_EXAM_WAIT == examStatus) {
            throw new ServiceException("考试未开始", HttpStatus.FORBIDDEN);
        }
        if(Constants.DEPT_EXAM_RUNNING != examStatus) {
            throw new ServiceException("考试未开始", HttpStatus.FORBIDDEN);
        }
    }

    /**
     * 对答案评分
     * @param answerQuestionList 题目回答
     */
    private List<AnswerStatus> markAnswer(List<AnswerQuestion> answerQuestionList) {
        Map<Long, Boolean> questionAnswerStatusMap = questionService.checkAnswer(answerQuestionList);
        return answerQuestionList.stream().map(answer -> {
            AnswerStatus answerStatus = new AnswerStatus();
            Long questionId = answer.getQuestionId();
            answerStatus.setQuestionId(questionId);
            answerStatus.setAnswerCorrect(questionAnswerStatusMap.getOrDefault(questionId, false));
            return answerStatus;
        }).collect(Collectors.toList());
    }

}
